package data

import (
	"commerce/common"
	"commerce/model"
	"commerce/req"
	"database/sql"
	"fmt"
	"strings"
	"sync"
	"time"
)

var workPool = sync.Pool{
	New: func() interface{} {
		return func(dto req.OrderReleaseStockDto, fn func(req.OrderReleaseStockDto)) {
			fn(dto)
		}
	},
}

func init() {
	workPool.Put(workPool.New())
	workPool.Put(workPool.New())
	workPool.Put(workPool.New())
}

func PageInventory(pageNo, pageSize int) (*common.Page, error) {

	row := common.DB.QueryRow("select count(*) FROM goods_inventory")
	var totalRecords int
	if err := row.Scan(&totalRecords); err != nil {
		return nil, err
	}
	stmt, err := common.DB.Prepare("select id, sku_id, sales, bought_user_num, stock, viewed, updated_time FROM goods_inventory LIMIT ?, ?")
	if err != nil {
		return nil, err
	}
	defer stmt.Close()

	rows, err := stmt.Query((pageNo-1)*pageSize, pageSize)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var is []model.GoodsInventory
	for rows.Next() {
		i := model.GoodsInventory{}
		if err := rows.Scan(&i.Id, &i.SkuId, &i.Sales, &i.BoughtUserNum, &i.Stock, &i.Viewed, &i.UpdatedTimeStr); err != nil {
			return nil, fmt.Errorf("goods inventory 分页数据有误:%s", err.Error())
		}
		is = append(is, i)
	}
	return common.NewPage(is, pageNo, pageSize, totalRecords), nil
}

func GetGoodsInventoryBySkuId(skuId int) (*model.GoodsInventory, error) {

	sqlTpl := "select id, sku_id, sales, bought_user_num, stock, viewed, updated_time FROM goods_inventory where sku_id = ?"

	stmt, err := common.DB.Prepare(sqlTpl)
	if err != nil {
		return nil, err
	}
	defer stmt.Close()

	var i model.GoodsInventory
	if err = stmt.QueryRow(skuId).Scan(&i.Id, &i.SkuId, &i.Sales, &i.BoughtUserNum, &i.Stock, &i.Viewed, &i.UpdatedTimeStr); err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, err
	}
	return &i, nil
}

func GetGoodsInventoryById(id int) (*model.GoodsInventory, error) {

	sqlTpl := "select id, sku_id, sales, bought_user_num, stock, viewed FROM goods_inventory where id = ?"

	stmt, err := common.DB.Prepare(sqlTpl)
	if err != nil {
		return nil, err
	}
	defer stmt.Close()

	var i model.GoodsInventory
	if err = stmt.QueryRow(id).Scan(&i.Id, &i.SkuId, &i.Sales, &i.BoughtUserNum, &i.Stock, &i.Viewed); err != nil {
		return nil, err
	}
	return &i, nil
}

func AddGoodsInventory(i model.GoodsInventory) (int, error) {

	sqlTpl := "insert into goods_inventory(sku_id, sales, bought_user_num, stock, viewed, updated_time) VALUES (?, ?, ?, ?, ?, ?)"
	result, err := common.DB.Exec(sqlTpl, i.SkuId, i.Sales, i.BoughtUserNum, i.Stock, i.Viewed, time.Now().Add(8*time.Hour))

	if err != nil {
		return 0, err
	}
	id, err := result.LastInsertId()

	if err != nil {
		return 0, fmt.Errorf("save goods inventory insert id err: %v", err)
	}
	return int(id), nil
}

func UpdateGoodsInventory(i model.GoodsInventory) (int, error) {

	stmt, err := common.DB.Prepare("update goods_inventory set sales = ?, bought_user_num = ?, stock = ?, viewed = ?, updated_time = ? WHERE id = ?")
	if err != nil {
		return 0, err
	}
	defer stmt.Close()

	result, err := stmt.Exec(i.Sales, i.BoughtUserNum, i.Stock, i.Viewed, time.Now().Add(8*time.Hour), i.Id)
	if err != nil {
		return 0, fmt.Errorf("update goods inventory err:%v", err.Error())
	}
	rowsAffected, err := result.RowsAffected()
	return int(rowsAffected), err
}

// -------------app专用接口开始---------------------------

func ListGoodsInventory(skuIds []int) ([]model.GoodsInventory, error) {

	sqlTpl := "select i.sku_id, i.stock-sales, k.real_price, k.main_img FROM goods_inventory i inner join goods_sku k on i.sku_id = k.id where i.sku_id in (?" + strings.Repeat(",?", len(skuIds)-1) + `)`

	stmt, err := common.DB.Prepare(sqlTpl)
	if err != nil {
		return nil, err
	}
	defer stmt.Close()

	args := make([]interface{}, len(skuIds))
	for i, id := range skuIds {
		args[i] = id
	}
	rows, err := stmt.Query(args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var gs []model.GoodsInventory
	for rows.Next() {
		g := model.GoodsInventory{}
		if err := rows.Scan(&g.SkuId, &g.Stock, &g.RealPrice, &g.MainImg); err != nil {
			return nil, fmt.Errorf("库存数据有误:%s", err.Error())
		}
		gs = append(gs, g)
	}
	return gs, nil
}

func LockOrderStock(tx *sql.Tx, orderItems []model.OrderItem, orderId int, orderNo string) error {

	// 保存库存工作单详情
	id, err := AddWareOrderTask(tx, model.WareOrderTask{OrderId: orderId, OrderNo: orderNo, TaskStatus: 1})
	if err != nil {
		return err
	}
	var result int
	for _, item := range orderItems {
		result, err = UpdateSkuStock(item.Count, item.SkuId)
		if err != nil {
			return err
		}
		// 扣减库存失败（db数据没有影响）[没库存了]
		if result == 0 {
			return fmt.Errorf("no stock for:%d", item.SkuId)
		}
		detail := model.WareOrderTaskDetail{TaskId: id, SkuId: item.SkuId,
			SkuNum: item.Count, SkuName: item.SkuName, LockStatus: 1}

		id, err := AddWareOrderTaskDetail(tx, detail)
		if err != nil {
			return err
		}
		detail.Id = id
		// 发mq 20分钟后，查询订单状态，如果还没付钱，我这边可要解锁库存了 @todo 先用定时器实现
		//setScheduledStockTask(req.OrderReleaseStockDto{TaskId: id, OrderTaskDetail: detail})
	}
	setScheduledStockTask2(req.OrderReleaseStockDto{TaskId: id})

	return nil
}

func UpdateSkuStock(num, skuId int) (int, error) {

	stmt, err := common.DB.Prepare("update goods_inventory set locked = locked + ?, updated_time = ? WHERE sku_id = ? and stock >= locked + ?")
	if err != nil {
		return 0, err
	}
	defer stmt.Close()

	result, err := stmt.Exec(num, time.Now().Add(8*time.Hour), skuId, num)
	if err != nil {
		return 0, fmt.Errorf("update UpdateSkuStock err:%v", err.Error())
	}
	rowsAffected, err := result.RowsAffected()
	return int(rowsAffected), err
}

func ReleaseSkuStock(num, skuId int) (int, error) {

	stmt, err := common.DB.Prepare("update goods_inventory set locked = locked - ?, updated_time = ? WHERE sku_id = ? and locked >= ?")
	if err != nil {
		return 0, err
	}
	defer stmt.Close()

	result, err := stmt.Exec(num, time.Now().Add(8*time.Hour), skuId, num)
	if err != nil {
		return 0, fmt.Errorf("ReleaseSkuStock err:%v", err.Error())
	}
	rowsAffected, err := result.RowsAffected()
	return int(rowsAffected), err
}

//func setScheduledStockTask(dto req.OrderReleaseStockDto) {
//
//	workPool.Get().(func(dto req.OrderReleaseStockDto,
//		fn func(req.OrderReleaseStockDto)))(dto, func(dto req.OrderReleaseStockDto) {
//		// 这是类似于异步的
//		time.AfterFunc(common.RELEASE_STOCK_TIME*time.Minute, func() {
//			common.Error..Println("setScheduledStockTask: taskId->", dto.TaskId)
//			taskDetail, err := GetWareOrderDetailById(dto.OrderTaskDetail.Id, 1)
//			if err != nil {
//				common.Error..Printf("1.setScheduledStockTask, dto:%v, err:%v", dto, err)
//				return
//			}
//			var task *model.WareOrderTask
//			task, err = GetWareOrderTaskById(taskDetail.TaskId, 1)
//			if err != nil {
//				common.Error..Printf("2.setScheduledStockTask err:%v", err)
//				return
//			}
//			var order *model.Order
//			order, err = GetOrderById(task.OrderId)
//			if err != nil && err != sql.ErrNoRows {
//				common.Error..Printf("3.setScheduledStockTask err:%v", err)
//				return
//			}
//			// 因事务回滚
//			if err == sql.ErrNoRows || order == nil || order.State == common.ORDER_STATE_CANCELLED {
//				// 解锁库存 & 更新 工作单明细
//				ReleaseSkuStock(taskDetail.SkuNum, taskDetail.SkuId)
//				UpdateWareOrderTaskDetailStatus(taskDetail.Id, 2)
//			}
//		})
//	})
//}

func setScheduledStockTask2(dto req.OrderReleaseStockDto) {

	workPool.Get().(func(dto req.OrderReleaseStockDto,
		fn func(req.OrderReleaseStockDto)))(dto, func(dto req.OrderReleaseStockDto) {
		// 这是类似于异步的
		time.AfterFunc(common.RELEASE_STOCK_TIME*time.Minute, func() {
			common.Info.Println("setScheduledStockTask: taskId->", dto.TaskId)
			task, err := GetWareOrderTaskById(dto.TaskId, 1)
			if err != nil {
				common.Error.Printf("2.setScheduledStockTask err:%v", err)
				return
			}
			var order *model.Order
			order, err = GetOrderById(task.OrderId)
			if err != nil && err != sql.ErrNoRows {
				common.Error.Printf("3.setScheduledStockTask err:%v", err)
				return
			}
			// 因事务回滚 || 已取消状态
			if err == sql.ErrNoRows || order == nil || order.State == common.ORDER_STATE_CANCELLED {
				details, err := ListWareOrderTaskDetailByTaskId(dto.TaskId, 1)

				if err == nil && details != nil && len(details) > 0 {

					for _, d := range details {
						// 解锁库存 & 更新 工作单明细
						ReleaseSkuStock(d.SkuNum, d.SkuId)
						UpdateWareOrderTaskDetailStatus(d.Id, 2)
					}
				}
			}
		})
	})
}
